package com.xusiyan08.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xusiyan08.entity.Item;
import com.xusiyan08.service.ItemService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;

@RestController
@RequestMapping("/api/items")
@CrossOrigin(origins = "*")
public class ItemController {

    private static final Logger logger = LoggerFactory.getLogger(ItemController.class);
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private ItemService itemService;

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<ResponseEntity<Object>> createItem(@RequestPart("title") String title,
                                                   @RequestPart("category") String category,
                                                   @RequestPart("price") String priceStr,
                                                   @RequestPart("description") String description,
                                                   @RequestPart("sellerId") String sellerIdStr,
                                                   @RequestPart("studentId") String studentId,
                                                   @RequestPart("primaryImageIndex") String primaryImageIndexStr,
                                                   @RequestPart(value = "images", required = false) Flux<FilePart> imageFiles) {

        logger.info("Received multipart/form-data request");
        logger.info("Form fields: title={}, category={}, price={}, description={}, sellerId={}, studentId={}, primaryImageIndex={}",
                title, category, priceStr, description, sellerIdStr, studentId, primaryImageIndexStr);

        // 验证和类型转换
        try {
            BigDecimal price = new BigDecimal(priceStr);
            Long sellerId = Long.parseLong(sellerIdStr);
            Integer primaryImageIndex = Integer.parseInt(primaryImageIndexStr);
                        Item.Category itemCategory = Item.Category.fromValue(category);

            // 处理图片文件
            return processImageFiles(imageFiles)
                    .flatMap(images -> {
                        // 创建 Item 对象
                        Item itemDetails = new Item();
                        itemDetails.setTitle(title);
                        itemDetails.setCategory(itemCategory);
                        itemDetails.setPrice(price);
                        itemDetails.setDescription(description);
                        itemDetails.setSellerId(sellerId);
                        itemDetails.setStudentId(studentId);
                        itemDetails.setPrimaryImageIndex(primaryImageIndex);

                        // 调用服务层（在IO线程池中执行阻塞操作）
                        return Mono.fromCallable(() ->
                                        itemService.createItem(itemDetails, sellerId, studentId, images, primaryImageIndex)
                                )
                                .subscribeOn(Schedulers.boundedElastic())
                                .map(savedItem -> ResponseEntity.ok((Object) savedItem))
                                .onErrorResume(e -> {
                                    logger.error("Error saving item in service", e);
                                    return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                                            .body((Object) "Error saving item: " + e.getMessage()));
                                });
                    });

        } catch (NumberFormatException e) {
            logger.error("Invalid number format", e);
            return Mono.just(ResponseEntity.badRequest().body("Invalid number format: " + e.getMessage()));
        } catch (IllegalArgumentException e) {
            logger.error("Invalid category value: {}", category, e);
            return Mono.just(ResponseEntity.badRequest().body("Invalid category value: " + category));
        }
    }

    // 处理图片文件的方法
    private Mono<MultipartFile[]> processImageFiles(Flux<FilePart> imageFiles) {
        if (imageFiles == null) {
            return Mono.just(new MultipartFile[0]);
        }

        return imageFiles
                .flatMap(this::convertFilePartToMultipartFile)
                .collectList()
                .map(list -> list.toArray(new MultipartFile[0]));
    }

    // 将 FilePart 转换为 MultipartFile
    private Mono<MultipartFile> convertFilePartToMultipartFile(FilePart filePart) {
        return Mono.fromCallable(() -> {
                    // 创建临时文件
                    Path tempFile = Files.createTempFile("upload_", "_" + filePart.filename());

                    // 将 FilePart 的内容写入临时文件
                    // 使用 then() 确保 transferTo 完成后再执行后续操作
                    return filePart.transferTo(tempFile)
                            .then(Mono.fromCallable(() -> {
                                // 创建自定义 MultipartFile 实现，并进行类型转换
                                MultipartFile multipartFile = new TempFileMultipartFile(
                                        filePart.name(),
                                        filePart.filename(),
                                        filePart.headers().getContentType() != null ?
                                                filePart.headers().getContentType().toString() : "application/octet-stream",
                                        tempFile.toFile()
                                );
                                return multipartFile; // 返回 MultipartFile 类型
                            }))
                            .block(); // 阻塞等待整个流程完成并返回结果
                })
                .subscribeOn(Schedulers.boundedElastic());
    }

    // 自定义 MultipartFile 实现
    private static class TempFileMultipartFile implements MultipartFile {
        private final String name;
        private final String originalFilename;
        private final String contentType;
        private final File file;

        public TempFileMultipartFile(String name, String originalFilename, String contentType, File file) {
            this.name = name;
            this.originalFilename = originalFilename;
            this.contentType = contentType;
            this.file = file;
        }

        private void processItemListForImageDisplay(List<Item> items) {
            if (items == null) {
                return;
            }
            for (Item item : items) {
                byte[] imagesBytes = item.getImages();
                if (imagesBytes != null && imagesBytes.length > 0) {
                    String base64Image = Base64.getEncoder().encodeToString(imagesBytes);
                    // 假设图片的格式是 JPEG。如果您的图片格式可能不同，您需要在保存时记录格式或尝试检测。
                    // 在这里为了简化，我们使用 JPEG 前缀。
                    item.setBase64Image("data:image/jpeg;base64," + base64Image);
                }
            }
        }


        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getOriginalFilename() {
            return originalFilename;
        }

        @Override
        public String getContentType() {
            return contentType;
        }

        @Override
        public boolean isEmpty() {
            return file.length() == 0;
        }

        @Override
        public long getSize() {
            return file.length();
        }

        @Override
        public byte[] getBytes() throws IOException {
            return Files.readAllBytes(file.toPath());
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return new FileInputStream(file);
        }

        @Override
        public void transferTo(File dest) throws IOException {
            Files.copy(file.toPath(), dest.toPath());
        }

        // 清理临时文件的方法
        public void cleanup() {
            if (file.exists()) {
                file.delete();
            }
        }
    }

    // 其他方法保持不变，但需要添加响应式处理
    @GetMapping
    public Mono<ResponseEntity<List<Item>>> getAllItems() {
        return Mono.fromCallable(() -> itemService.getAllItems())
                .subscribeOn(Schedulers.boundedElastic())
                .map(ResponseEntity::ok);
    }

    @GetMapping("/{itemId}")
    public Mono<ResponseEntity<Item>> getItemById(@PathVariable Long itemId) {
        return Mono.fromCallable(() -> itemService.getItemById(itemId))
                .subscribeOn(Schedulers.boundedElastic())
                .map(item -> {
                    if (item != null) {
                        // Convert image byte array to Base64 string for frontend display
                        byte[] imagesBytes = item.getImages();
                        if (imagesBytes != null && imagesBytes.length > 0) {
                            String base64Image = Base64.getEncoder().encodeToString(imagesBytes);
                            // Assuming the images are JPEGs for the Base64 prefix
                            // This might need adjustment based on actual image types
                            item.setBase64Image("data:image/jpeg;base64," + base64Image);
                        }
                    }
                    return item != null ? ResponseEntity.ok(item) : ResponseEntity.notFound().build();
                });
    }

    @GetMapping("/seller/{sellerId}")
    public Mono<ResponseEntity<List<Item>>> getItemsBySeller(@PathVariable Long sellerId) {
        return Mono.fromCallable(() -> itemService.getItemsBySeller(sellerId))
                .subscribeOn(Schedulers.boundedElastic())
                .map(ResponseEntity::ok);
    }

    @GetMapping("/search")
    public Mono<ResponseEntity<List<Item>>> searchItems(@RequestParam String keyword) {
        logger.info("Received request to search items with keyword: {}", keyword);
        return Mono.fromCallable(() -> itemService.searchItems(keyword))
                .subscribeOn(Schedulers.boundedElastic())
                .map(items -> ResponseEntity.ok(items))
                .onErrorResume(e -> {
                    logger.error("Error searching items: {}", keyword, e);
                    return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null));
                });
    }

    @GetMapping("/by-status")
    public Mono<ResponseEntity<List<Item>>> getItemsByStatus(@RequestParam("status") Item.Status status) {
        logger.info("Received request to get items by status: {}", status);
        return Mono.fromCallable(() -> itemService.getItemsByStatus(status))
                .subscribeOn(Schedulers.boundedElastic())
                .map(items -> ResponseEntity.ok(items))
                .onErrorResume(e -> {
                    logger.error("Error fetching items by status: {}", status, e);
                    return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null)); // Or a more specific error response
                });
    }

    @PutMapping(value = "/{itemId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<ResponseEntity<Item>> updateItem(@PathVariable Long itemId,
                                                 @RequestPart("itemDetails") String itemDetailsJson,
                                                 @RequestPart(value = "images", required = false) Flux<FilePart> imageFiles) {

        // TODO: Implement secure way to get current authenticated user ID
        // This is a placeholder - replace with your actual authentication mechanism
        Long currentUserId = getCurrentAuthenticatedUserId(); // Replace with actual method to get user ID

        return processImageFiles(imageFiles)
                .flatMap(images -> {
                    try {
                        // Parse item details JSON string to Item object
                        Item itemDetails = objectMapper.readValue(itemDetailsJson, Item.class);
                        itemDetails.setItemId(itemId); // Ensure correct item ID is used

                        return Mono.fromCallable(() -> itemService.updateItem(itemId, itemDetails, images, currentUserId)) // Pass currentUserId
                                .subscribeOn(Schedulers.boundedElastic())
                                .map(updatedItem -> ResponseEntity.ok(updatedItem))
                                .onErrorResume(RuntimeException.class, e -> { // Catch RuntimeException for permission denied
                                    logger.error("Error updating item with id: {} by user {}: {}", itemId, currentUserId, e.getMessage());
                                    if (e.getMessage().equals("You do not have permission to update this item.")) {
                                        return Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build()); // 403 Forbidden
                                    }
                                    return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
                                })
                                .onErrorResume(e -> { // Catch other errors
                                    logger.error("Unexpected error updating item with id: {} by user {}", itemId, currentUserId, e);
                                    return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
                                });
                    } catch (IOException e) {
                        logger.error("Error parsing item details JSON for item id: {}", itemId, e);
                        return Mono.just(ResponseEntity.badRequest().build());
                    }
                });
    }

    // TODO: Replace this placeholder method with your actual logic to get the authenticated user's ID
    private Long getCurrentAuthenticatedUserId() {
        logger.warn("Using placeholder/insecure method to get current user ID. REPLACE THIS IN PRODUCTION!");
        return 1L; // Dummy user ID - REPLACE WITH SECURE AUTHENTICATED USER ID
    }

    @DeleteMapping("/{itemId}")
    public Mono<ResponseEntity<String>> deleteItem(@PathVariable Long itemId) {
        return Mono.fromRunnable(() -> itemService.deleteItem(itemId))
                .subscribeOn(Schedulers.boundedElastic())
                .then(Mono.just(ResponseEntity.ok("Item deleted successfully")))
                .onErrorResume(e -> {
                    logger.error("Error deleting item with id: {}", itemId, e);
                    return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error deleting item: " + e.getMessage()));
                });
    }
}